﻿// ----------------------------------------------------------------------------
//  Copyright (C) 成都联宇创新科技有限公司 版权所有。 
//      
//  文件名：AdvancedEncryptionStandard.cs
//  文件功能描述：
//  创建标识：骆智慧 2016/03/02 23:28
//     
//  修改标识：骆智慧  2016/03/10/06:40
//  修改描述：
//  ----------------------------------------------------------------------------

#region 导入名称空间

using System;
using System.IO;
using SysCrypto = System.Security.Cryptography;

#endregion

namespace LightWork.Cryptography
{
    /// <summary>
    /// 高级加密标准（AES）
    /// </summary>
    public class AdvancedEncryptionStandard : CryptoBase
    {
        #region --------------加密-------------

        /// <summary>
        /// 加密数据
        /// </summary>
        /// <param name="plaintext">明文</param>
        /// <param name="password">用于派生密钥的密码</param>
        /// <param name="salt">用于派生密钥的密钥salt</param>
        /// <param name="iterations">操作的迭代数</param>
        /// <remarks>salt 大小必须等于或大于 8 字节，并且迭代次数必须大于零。 建议的最小迭代次数为 1000</remarks>
        /// <returns></returns>
        public override byte[] Encrypt(byte[] plaintext, byte[] password, byte[] salt, int iterations)
        {
            using (var memoryStream = new MemoryStream())
            {
                using (var rijndeal = new SysCrypto.RijndaelManaged())
                {
                    //通过使用密码、salt 值和迭代次数派生密钥
                    var deriveBytes = new SysCrypto.Rfc2898DeriveBytes(password, salt, iterations);

                    rijndeal.Mode = SysCrypto.CipherMode.CBC;
                    rijndeal.KeySize = 256;
                    rijndeal.BlockSize = 128;
                    rijndeal.Key = deriveBytes.GetBytes(rijndeal.KeySize / 8);
                    rijndeal.IV = deriveBytes.GetBytes(rijndeal.BlockSize / 8);

                    using (
                        var cryptoStream = new SysCrypto.CryptoStream(memoryStream, rijndeal.CreateEncryptor(),
                            SysCrypto.CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(plaintext, 0, plaintext.Length);
                        cryptoStream.Flush();
                    }


                    return memoryStream.ToArray();
                }
            }
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="plaintextBytes">明文</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public override string Encrypt(byte[] plaintextBytes, byte[] password)
        {
            password = CryptoUtility.GetHashAlgorithm(HashAlgorithmEnum.SHA256).ComputeHash(password);

            var saltSize = CryptoUtility.GetSaltSize(password, 1000);
            var saltBytes = CryptoUtility.GetRandomBytes(saltSize);

            //原始字节码附加Salt值
            var bytesToBeEncrypted = new byte[saltBytes.Length + plaintextBytes.Length];
            for (var i = 0; i < saltBytes.Length; i++)
                bytesToBeEncrypted[i] = saltBytes[i];
            for (var i = 0; i < plaintextBytes.Length; i++)
                bytesToBeEncrypted[i + saltBytes.Length] = plaintextBytes[i];

            var encryptedBytes = Encrypt(bytesToBeEncrypted, password, DefaultSalt, 1000);
            return BitConverter.ToString(encryptedBytes).Replace("-", "");
        }

        #endregion

        #region --------------解密-------------

        /// <summary>
        /// 解密数据
        /// </summary>
        /// <param name="ciphertext">密文</param>
        /// <param name="password">用于派生密钥的密码</param>
        /// <param name="salt">用于派生密钥的密钥salt</param>
        /// <param name="iterations">操作的迭代数</param>
        /// <remarks>salt 大小必须等于或大于 8 字节，并且迭代次数必须大于零。 建议的最小迭代次数为 1000</remarks>
        /// <returns></returns>
        public override byte[] Decrypt(byte[] ciphertext, byte[] password, byte[] salt, int iterations)
        {
            using (var memoryStream = new MemoryStream())
            {
                using (var rijndeal = new SysCrypto.RijndaelManaged())
                {
                    var deriveBytes = new SysCrypto.Rfc2898DeriveBytes(password, salt, iterations);

                    rijndeal.Mode = SysCrypto.CipherMode.CBC;
                    rijndeal.KeySize = 256;
                    rijndeal.BlockSize = 128;
                    rijndeal.Key = deriveBytes.GetBytes(rijndeal.KeySize / 8);
                    rijndeal.IV = deriveBytes.GetBytes(rijndeal.BlockSize / 8);

                    using (
                        var cryptoStream = new SysCrypto.CryptoStream(memoryStream, rijndeal.CreateDecryptor(),
                            SysCrypto.CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(ciphertext, 0, ciphertext.Length);
                        cryptoStream.Flush();
                    }


                    return memoryStream.ToArray();
                }
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="ciphertext">密文</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public override string Decrypt(byte[] ciphertext, byte[] password)
        {
            using (var sha256 = SysCrypto.SHA256.Create())
            {
                password = sha256.ComputeHash(password);
                var decryptedBytes = Decrypt(ciphertext, password, DefaultSalt, 1000);
                var saltSize = CryptoUtility.GetSaltSize(password, 1000);

                // 去盐值还原数据
                var originalBytes = new byte[decryptedBytes.Length - saltSize];
                for (var i = saltSize; i < decryptedBytes.Length; i++)
                    originalBytes[i - saltSize] = decryptedBytes[i];

                return FromBytes(originalBytes);
            }
        }

        #endregion
    }
}